#include <stdio.h>
#include <string.h>

#include "SEGGER_RTT.h"

#include "FreeRTOS.h"
#include "task.h"

#include "misc.h"

#include "base16.h"
#include "base64.h"

#include "drv_bc26.h"

#include "drv_m5310a.h"



void drv_m5310a_csq(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    MSG_AT msg_at;
    uint32_t rssi, ber;
    if (at_match(&bytes, "+u,+u", &rssi, &ber)) {
        return;
    }
    msg_at.info = AT_INFO_CSQ;
    msg_at.value = rssi;
    env_net.sq = rssi;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass csq msg");
    }
}

//+CEREG:<n>,<stat>
void drv_m5310a_cereg(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    MSG_AT msg_at;
    uint32_t status;
    if (at_match(&bytes, "+u,+u", NULL, &status)) {
        return;
    }
    if ((status == 1) || (status == 5)) {
        msg_at.info = AT_INFO_CEREG_OK;
        if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
            NET_ERR("failed to pass cereg msg");
        }
    }
}

void drv_m5310a_npsmr(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    uint32_t n, status;
    MSG_AT msg_at;
    if (at_match(&bytes, "+u,+u", &n, &status) == 0) {
        if (status) {
            msg_at.info = AT_INFO_NPSMR;
            if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
                NET_ERR("failed to pass cereg msg");
            }
        }
    } else {
        bytes.pdata = pdata;
        bytes.len = len;
        if (at_match(&bytes, "+u", &status) == 0) {
            if (status) {
                NET_INF("enter psm");
            } else {
                NET_INF("exit psm");
            }
        }
    }
}

void drv_m5310a_connect_ok(uint8_t *pdata, uint32_t len) {
    MSG_AT msg_at;
    msg_at.info = AT_INFO_CONNECT_OK;
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass msg");
    }
}

int32_t drv_m5310a_socket_map(int32_t socket) {
    uint32_t i;
    for (i = 0; i < DRV_NET_MAX_NUM_OF_CONNECTIONS; i++) {
        if (env_net.pconns[i].socket == socket) {
            return i;
        }
    }
    return -1;
}

//<socket>,<ip_addr>,<port>,<length>,<data>,<remaining_length>
//+NSORF:0,59.174.243.135,161,5,3131313133,0
void drv_m5310a_nsorf(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    uint32_t recv_len;
    MSG_RECV msg_recv;
    if (at_match(&bytes, "+d,+s,+s,+u,", &socket_id, NULL, NULL, &recv_len)) {
        return;
    }
    socket_id = drv_m5310a_socket_map(socket_id);
    if (!BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket:%d", socket_id);
        return;
    }
    recv_len <<= 1;
    if (recv_len > bytes.len) {
        NET_ERR("invalid length %d,%d", recv_len << 1, bytes.len);
        return;
    }
    msg_recv.type = MSG_RECV_SOCKET;
    msg_recv.value = socket_id;
    recv_len = base16_decode(bytes.pdata, recv_len, bytes.pdata, recv_len);
    if (recv_len == 0) {
        NET_ERR("failed to decode");
        return;
    }
    app_fifo_write(&(env_net.pconns[socket_id].fifo), bytes.pdata, &recv_len);
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_ERR("failed to pass recv");
    }
}


void drv_m5310a_nsocli(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    if (at_match(&bytes, "+d", &socket_id)) {
        return;
    }
    socket_id = drv_m5310a_socket_map(socket_id);
    if (!BETWEEN11(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket:%d", socket_id);
        return;
    }
    NET_INF("close socket %d", socket_id);
    //todo:close socket
}

void drv_m5310a_cgsn(uint8_t *pdata, uint32_t len) {
    pdata[len] = '\0';
    if (len >= sizeof(env_net.pimei)) {
        NET_INF("invalid imei:%s", pdata);
        return;
    }
    strcpy(env_net.pimei, (const char *)pdata);
    NET_INF("imei:%s", pdata);
}

//onenet
void drv_m5310a_miplcreate(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    MSG_AT msg_at;
    int32_t csi;
    if (at_match(&bytes, "+d", &csi)) {
        return;
    }
    if (csi == 0) {
        msg_at.info = AT_INFO_CSI_OK;
    } else {
        msg_at.info = AT_INFO_CSI_ERROR;
    }
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the info");
    }
}

//+MIPLEVENT:0,6
void drv_m5310a_miplevent(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    MSG_AT msg_at;
    int32_t csi;
    uint32_t event_id;
    if (at_match(&bytes, "+d,+u", &csi, &event_id)) {
        return;
    }
    NET_INF("reg:%d,%d", csi, event_id);
    if (csi != 0) {
        NET_ERR("invalid csi %d", csi);
        return;
    }
    if (event_id == 6) {
        msg_at.info = AT_INFO_REG_SUCCESS;
    } else if (event_id == 7) {
        msg_at.info = AT_INFO_REG_FAILED;
    } else {
        NET_WAR("unsupport even id:%d", event_id);
        return;
    }
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("fail to pass the info");
    }
}

//+MIPLOBSERVE:0,44402,1,3348,0,-1
//+MIPLOBSERVE:0,44402,1,3348,1,-1
void drv_m5310a_miplobserve(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t csi, inst_id;
    uint32_t obj_id;

    if (at_match(&bytes, "+d,+u,+u,+u,+d", &csi, NULL, NULL, &obj_id, &inst_id)) {
        return;
    }
    if (csi != 0) {
        NET_ERR("invalid csi%d", csi);
        return;
    }
    if (obj_id != CM_OBJ_ID) {
        NET_ERR("invalid obj_id:%d", obj_id);
        return;
    }
    if (inst_id == CM_INS_ID_UP) {
        NET_INF("observe the upload instance");
    } else if (inst_id == CM_INS_ID_DOWN) {
        NET_INF("observe the download instance");
    }
    //todo:observe mid
}

//+MIPLDISCOVER:0,27268,3348
void drv_m5310a_mipldiscover(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t csi;
    uint32_t obj_id;
    MSG_AT msg_at;
    if (at_match(&bytes, "+d,+u,+u", &csi, NULL, &obj_id)) {
        return;
    }
    if (csi != 0) {
        NET_ERR("invalid csi%d", csi);
        return;
    }
    if (obj_id != CM_OBJ_ID) {
        NET_ERR("invalid obj_id:%d", obj_id);
        return;
    }
    msg_at.info = AT_INFO_DISCOVER;
    //todo:discover mid
    if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
        NET_ERR("failed to pass the discover message");
    }
}
//+MIPLWRITE:<ref>,<mid>,<objid>,<insid>,<resid>,<type>,<len>,<value>,<flag>,<index>
//+MIPLWRITE:0,46122,3348,1,5750,2,6,313131,0
//+MIPLWRITE:0,12823,3348,1,5750,2,8,4D544578,0
void drv_m5310a_miplwrite(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    uint32_t mid;
    int32_t csi;
    int32_t obj_id, inst_id, res_id;
    MSG_RECV msg_recv;
    uint32_t data_type, data_len;
    if (at_match(&bytes, "+d,+u,+d,+d,+d,", &csi, &mid, &obj_id, &inst_id, &res_id)) {
        NET_ERR("failed to match");
        return;
    }
    if ((csi != 0) ||
        (obj_id != CM_OBJ_ID) ||
        (inst_id != CM_INS_ID_DOWN) ||
        (res_id != CM_RES_ID)) {
        pdata[len] = '\0';
        NET_ERR("invalid write:%s", pdata);
        return;
    }
    if (at_match(&bytes, "+u,+u,", &data_type, &data_len)) {
        return;
    }
    if (data_type != 2) {
        NET_INF("not support data type %d", data_type);
        return;
    }
//  pdata[len] = '\0';
//  NET_INF("len=%d,%s",data_len,bytes.pdata);
    data_len = base16_decode(bytes.pdata, data_len, bytes.pdata, data_len);
    if (data_len == 0) {
        NET_ERR("failed to decode by base16");
        return;
    }
    data_len = base64_decode(bytes.pdata, data_len, bytes.pdata, data_len);
    if (data_len == 0) {
        NET_ERR("failed to decode by base64");
        return;
    }
    msg_recv.type = MSG_RECV_WRITE;
    msg_recv.value = mid;
//  NET_INF("mid=%d",mid);
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_WAR("failed to pass the write msg");
    }
//  NET_INF("len=%d",data_len);
    app_fifo_write(&(env_net.pconns[0].fifo), bytes.pdata, &data_len);
    msg_recv.type = MSG_RECV_SOCKET;
    msg_recv.value = 0;
    if (xQueueSend(env_net.qrecv, &msg_recv, 200) != pdPASS) {
        NET_WAR("failed to pass the socket msg");
    }
}

int32_t drv_m5310a_any(uint8_t *pdata, uint32_t len) {
    BYTES bytes = { .pdata = pdata, .len = len };
    int32_t socket_id;
    MSG_AT msg_at;
    if ((len == 1) && BETWEEN11('0', *pdata, '9')) {              //socket alloc
        socket_id = *pdata - '0';
        if (BETWEEN11(0, socket_id, 6)) {
            msg_at.info = AT_INFO_SOCKET_ALLOC;
            msg_at.value = socket_id;
            if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
                NET_ERR("failed to pass info");
            }
        } else {
            NET_ERR("invalid socket id:%d", socket_id);
        }
        return 0;
    } else if (!str_all_number_pre((const char *)pdata, len)) { //imei
        pdata[len] = '\0';
        NET_INF("imsi:%s", pdata);
        if (len >= sizeof(env_net.pimsi)) {
            NET_INF("invalid imsi:%s", pdata);
            return -1;
        }
        strcpy(env_net.pimsi, (const char *)pdata);
        if (strncmp((const char *)pdata, "46004", sizeof("460xx") - 1) == 0) {
            env_net.carrier = DRV_NET_CARRIER_CM;
        } else if (strncmp((const char *)pdata, "46002", sizeof("460xx") - 1) == 0) {
            env_net.carrier = DRV_NET_CARRIER_CM;
        } else if (strncmp((const char *)pdata, "46000", sizeof("460xx") - 1) == 0) {
            env_net.carrier = DRV_NET_CARRIER_CM;
        } else if (strncmp((const char *)pdata, "46007", sizeof("460xx") - 1) == 0) {
            env_net.carrier = DRV_NET_CARRIER_CM;
        } else if (strncmp((const char *)pdata, "46011", sizeof("460xx") - 1) == 0) {
            env_net.carrier = DRV_NET_CARRIER_CT;
        } else if (strncmp((const char *)pdata, "46003", sizeof("460xx") - 1) == 0) {
            env_net.carrier = DRV_NET_CARRIER_CT;
        } else if (strncmp((const char *)pdata, "46005", sizeof("460xx") - 1) == 0) {
            env_net.carrier = DRV_NET_CARRIER_CT;
        } else {
            env_net.carrier = DRV_NET_CARRIER_MAX;
        }
        NET_INF("current carrier is %d", env_net.carrier);
        return 0;
    } else if (!at_match(&bytes, "+u,+u", &socket_id, NULL)) {     //socket send
        if (BETWEEN11(0, socket_id, 6)) {
            msg_at.info = AT_INFO_SOCKET_SEND_OK;
            socket_id = drv_m5310a_socket_map(socket_id);
            if (BETWEEN10(0, socket_id, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
                msg_at.value = socket_id;
                if (xQueueSend(env_net.qinfo, &msg_at, 200) != pdPASS) {
                    NET_ERR("failed to send info");
                }
            } else {
                NET_ERR("find no matched socket");
            }
        } else {
            NET_ERR("invalid socket:%d", socket_id);
        }
        return 0;
    } else {
        return -1;
    }
}



extern void drv_bc26_miplread(uint8_t *pdata, uint32_t len);

AT_HANDLER drv_m5310a_at_handlers[] = {
    AT_HANDLER_GENERAL_DEF("OK", drv_net_result_ok),
    AT_HANDLER_GENERAL_DEF("ERROR", drv_net_result_error),
    AT_HANDLER_GENERAL_DEF("+CSQ:", drv_m5310a_csq),
    AT_HANDLER_GENERAL_DEF("+CEREG:", drv_m5310a_cereg),
    AT_HANDLER_GENERAL_DEF("CONNECT OK", drv_m5310a_connect_ok),
    AT_HANDLER_GENERAL_DEF("+NSORF:", drv_m5310a_nsorf),
    AT_HANDLER_GENERAL_DEF("+NSOCLI:", drv_m5310a_nsocli),
    AT_HANDLER_GENERAL_DEF("+CGSN:", drv_m5310a_cgsn),
    AT_HANDLER_GENERAL_DEF("+NPSMR:", drv_m5310a_npsmr),
    //onenet
    AT_HANDLER_GENERAL_DEF("+MIPLCREATE:", drv_m5310a_miplcreate),
    AT_HANDLER_GENERAL_DEF("+MIPLEVENT:", drv_m5310a_miplevent),
    AT_HANDLER_GENERAL_DEF("+MIPLOBSERVE:", drv_m5310a_miplobserve),
    AT_HANDLER_GENERAL_DEF("+MIPLDISCOVER:", drv_m5310a_mipldiscover),
    AT_HANDLER_GENERAL_DEF("+MIPLWRITE:", drv_m5310a_miplwrite),
//  {CSTRING("+MIPLREAD"),drv_bc26_miplread},
    AT_HANDLER_ANY(drv_m5310a_any)
};

//void


NET_ERR_CODE drv_m5310a_try_to_get_ip(void) {
    uint32_t i;
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    vTaskDelay(4000);
    ret = drv_net_write_bytes_raw(1000, 15, TSTRING(uint8_t *,"AT\r"));
    if (ret < 0) {
        NET_ERR("module might be broken");
        return ret;
    }
//  ret = drv_net_write_bytes_raw(1000,15,TSTRING(uint8_t *,"AT+NCCID\r"));
//  if(ret < 0){
//      NET_ERR("module might be broken");
//      return ret;
//  }
    for (i = 20; i > 0; i--) {
        ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CIMI\r"));
        if (ret == NET_ERR_OK) {
            break;
        } else if (i == 1) {
            NET_ERR("sim card initialization timeout");
            return NET_ERR_NO_CARD;
        } else {
            vTaskDelay(500);
        }
    }

    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+CGSN=1\r"));
    if (ret < 0) {
        NET_ERR("the module might be broken");
        return ret;
    }

    for (i = 20; i > 0; i--) {
        xQueueReset(env_net.qinfo);
        ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CSQ\r"));
        if (ret != NET_ERR_OK) {
            NET_ERR("");
            continue;
        }
        if (xQueueReceive(env_net.qinfo, &msg_at, 100) == pdPASS) {
            if (msg_at.info == AT_INFO_CSQ) {
                NET_INF("current rssi%d:%d", i, msg_at.value);
                if ((msg_at.value > 12) && (msg_at.value != 99)) {
                    break;
                }
            } else {
                NET_WAR("unexpected msg info:%08x", msg_at.info);
            }
        }
        if (i == 1) {
            NET_ERR("signal is not good");
            return NET_ERR_UNKOWN;
        }
        vTaskDelay(500);
    }


    xQueueReset(env_net.qinfo);
    for (i = 40; i > 0; i--) {
        NET_INF("request registration status%02d", i);
        ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CEREG?\r"));
        if (ret < 0) {
            NET_ERR("the module might be broken");
            return NET_ERR_UNKOWN;
        }
        if (xQueueReceive(env_net.qinfo, &msg_at, 100) == pdPASS) {
            if (msg_at.info == AT_INFO_CEREG_OK) {
                NET_INF("registered successfully");
                break;
            } else {
                NET_WAR("unexpected msg info:%08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("registration timeout");
            return NET_ERR_UNKOWN;
        }
        vTaskDelay(1000);
    }

    drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+CGACT?\r"));
    drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+CGATT?\r"));

    ret = drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+NPSMR=1\r"));
    if (ret < 0) {
        NET_INF("failed to enable the unsolicited result code");
        return ret;
    }

    ret = drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CPSMS=1,,,01000100,00001010\r"));
    if (ret < 0) {
        NET_INF("failed to enable the psm");
        return ret;
    }

//  drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CGDCONT=1,\"IP\",\"CMNBIOT\"\r"));
//  drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CFUN?\r"));
//  ret = drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CFUN=1\r"));
//  drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CGDCONT=1,\"IP\",\"CMNBIOT\"\r"));
//  drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CGACT=1,1\r"));
//  if(ret < 0){
//      NET_ERR("module might be broken");
//      return ret;
//  }
//  ret = drv_net_write_bytes_raw(1000,15,TSTRING(uint8_t *,"AT+CGMI\r"));
//  if(ret < 0){
//      NET_ERR("module might be broken");
//      return ret;
//  }
//  ret = drv_net_write_bytes_raw(1000,15,TSTRING(uint8_t *,"AT+CGPADDR?\r"));
//  if(ret < 0){
//      NET_ERR("module might be broken");
//      return ret;
//  }
    //request carrier
    ret = drv_net_write_bytes_raw(1000,1,TSTRING(uint8_t *,"AT+CGDCONT?\r"));
    if (ret < 0) {
        NET_INF("failed to read carrier");
        return ret;
    }
    return NET_ERR_OK;
}


NET_ERR_CODE drv_m5310a_socket_connect(int32_t socket,
                                       DRV_NET_SOCKET_TYPE service_type,
                                       char *pstr_ip,
                                       char *pstr_port,
                                       uint32_t fifo_size) {
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    char pbuf[64];
    int32_t len;
    uint32_t i;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket");
        return NET_ERR_UNKOWN;
    }
    if ((service_type != DRV_NET_SOCKET_TCP) && (service_type != DRV_NET_SOCKET_UDP)) {
        NET_ERR("invalid service type%d", service_type);
        return NET_ERR_UNKOWN;
    }
    if ((pstr_ip == NULL) || (pstr_port == NULL)) {
        NET_ERR("");
        return NET_ERR_UNKOWN;
    }
    //init fifo
    ret = drv_net_fifo_init(&(env_net.pconns[socket].fifo), fifo_size);
    if (ret < 0) {
        NET_ERR("unknown");
        return ret;
    }

    //AT+NSOCO=0,59.174.243.135,161
    len = snprintf(pbuf, len, "AT+NSOCO=0,%s,%s\r", pstr_ip, pstr_port);
    if (len < 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }

    //AT+NSOCR=<type>,<protocol>[,<listenport>[,<receive control>
    //AT+NSOCR="DGRAM",17,0,2 //UDP
    //AT+NSOCR="STREAM",6,0,2 //TCP
    xQueueReset(env_net.qinfo);
    if (service_type == DRV_NET_SOCKET_TCP) {
        ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+NSOCR=\"STREAM\",6,0,2\r"));
    } else if (service_type == DRV_NET_SOCKET_UDP) {
        ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t *,"AT+NSOCR=\"DGRAM\",17,0,2\r"));
    }
    //todo:service type
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }

    //read the allocated socket
    for (i = 10; i > 0; i--) {
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_SOCKET_ALLOC) {
                NET_INF("allocated socket successfully %d<-->%d", socket, msg_at.value);
                env_net.pconns[socket].socket = msg_at.value;
                if (service_type == DRV_NET_SOCKET_TCP) {
                    env_net.pconns[socket].type = DRV_NET_SERVICE_TCP;
                } else {
                    env_net.pconns[socket].type = DRV_NET_SERVICE_UDP;
                }
                break;
            } else {
                NET_WAR("unexpected info:%08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("failed to creat a new socket");
            return NET_ERR_UNKOWN;
        }
    }

    if (service_type == DRV_NET_SOCKET_UDP) {
        strcpy(env_net.pconns[socket].paddr, pstr_ip);
        strcpy(env_net.pconns[socket].pport, pstr_port);
        return NET_ERR_OK;
    }
    //else
    pbuf[sizeof("AT+NSOCO=")- 1] = env_net.pconns[socket].socket + '0';
    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(1000, 1,(uint8_t *)pbuf,len);
    if (ret < 0) {
        NET_ERR("");
        //todo:close the socket
        return ret;
    }


    for (i = 20; i > 0; i--) {
        NET_INF("try to connect socket %d", socket);
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_CONNECT_OK) {
                break;
            } else {
                NET_WAR("unexpected info:%08x", socket);
            }
        } else if (i == 1) {
            NET_ERR("socket %d connection timeout", socket);
            //todo:close the socket
            return NET_ERR_UNKOWN;
        }
    }
    return NET_ERR_OK;
}


NET_ERR_CODE drv_m5310a_socket_send(int32_t socket, uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t pbuf_len;
    NET_CONNECTION *pconn;
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    uint32_t i;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalide socket");
        return NET_ERR_UNKOWN;
    }
    if (pdata == NULL) {
        NET_ERR("null pointer");
        return NET_ERR_UNKOWN;
    }
    pbuf = pvPortMalloc((len << 1) + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to alloc");
        return NET_ERR_UNKOWN;
    }
    pconn = &(env_net.pconns[socket]);
    if (pconn->type == DRV_NET_SERVICE_UDP) {
        pbuf_len = snprintf((char *)pbuf, 64, "AT+NSOST=%d,%s,%s,%d,", pconn->socket, pconn->paddr, pconn->pport, len);
    } else {
        pbuf_len = snprintf((char *)pbuf, 64, "AT+NSOSD=%d,%d,", pconn->socket, len);
    }
    if (pbuf_len < 0) {
        NET_ERR("failed to format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    len = base16_encode(pdata, len, pbuf + pbuf_len, len << 1);
    pbuf_len += len;
    memcpy(pbuf + pbuf_len, CSTRING("\r"));
    pbuf_len += (sizeof("\r") - 1);


    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(10000, 1, pbuf, pbuf_len);
    if (ret < 0) {
        vPortFree(pbuf);
        return ret;
    }

    for (i = 20; i > 0; i--) {
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_SOCKET_SEND_OK) {
                if (msg_at.value == socket) {
                    break;
                }
            } else {
                NET_WAR("unexpected info:%08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("socket %d transmission timeout", socket);
            vPortFree(pbuf);
            return NET_ERR_UNKOWN;
        }
    }
    vPortFree(pbuf);
    return NET_ERR_OK;
}


NET_ERR_CODE drv_m5310a_socket_disconnect(int32_t socket) {
    char pbuf[64];
    int32_t len;
    NET_ERR_CODE ret;
    if (!BETWEEN10(0, socket, DRV_NET_MAX_NUM_OF_CONNECTIONS)) {
        NET_ERR("invalid socket:%d", socket);
        return NET_ERR_UNKOWN;
    }
    len = snprintf(pbuf, len, "AT+NSOCL=%d\r", env_net.pconns[socket].socket);
    if (len < 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }
    ret = drv_net_write_bytes_raw(10000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("failed to close socket %d", socket);
        return ret;
    }
    env_net.pconns[socket].socket = -1;
    return NET_ERR_OK;
}


NET_ERR_CODE drv_m5310a_cm_connect(char *pstr_ip, char *pstr_port, uint32_t fifo_size) {
    char pbuf[64];
    int32_t len;
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    uint32_t i;
    //init fifo
    ret = drv_net_fifo_init(&(env_net.pconns[0].fifo), fifo_size);
    if (ret < 0) {
        NET_ERR("unknown");
        return ret;
    }
    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(3000, 1, TSTRING(uint8_t * , "AT+MIPLCREATE=49,130031F10003F2002304001100000000000000123138332E3233302E34302E34303A35363833000131F30008C000000000,0,49,0\r"));
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }
    for (i = 5; i > 0; i--) {
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_CSI_OK) {
                break;
            } else if (msg_at.info == AT_INFO_CSI_ERROR) {
                NET_ERR("failed to creat onenet csi");
                return NET_ERR_UNKOWN;
            } else {
                NET_WAR("unexpected info %08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("create csi timeout");
            return NET_ERR_UNKOWN;
        }
    }
    //AT+MIPLADDOBJ=0,3348,2,"11",0,0
    len = snprintf(pbuf, sizeof(pbuf), "AT+MIPLADDOBJ=0,%d,2,\"11\",0,0\r", CM_OBJ_ID);
    if (len < 0) {
        NET_ERR("failed to format");
        return NET_ERR_UNKOWN;
    }
    ret = drv_net_write_bytes_raw(3000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }

    //AT+MIPLDISCOVERRSP=0,3348,1,4,"5750"
    len = snprintf(pbuf, sizeof(pbuf), "AT+MIPLDISCOVERRSP=0,%d,1,4,\"%s\"\r", CM_OBJ_ID, CM_RES_ID_STR);
    if (len < 0) {
        NET_ERR("format");
        return NET_ERR_UNKOWN;
    }
    ret = drv_net_write_bytes_raw(3000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }

    //upload
    //AT+MIPLNOTIFY=0,0,3348,0,5750,1,4,"test",0,0
    len = snprintf(pbuf, sizeof(pbuf), "AT+MIPLNOTIFY=0,0,%d,%d,%s,1,4,\"test\",0,0\r", CM_OBJ_ID, CM_INS_ID_UP, CM_RES_ID_STR);
    if (len < 0) {
        NET_ERR("format");
        return NET_ERR_UNKOWN;
    }
    ret = drv_net_write_bytes_raw(3000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }
    //download
    //AT+MIPLNOTIFY=0,0,3348,1,5750,1,4,"test",0,0

    //AT+MIPLOPEN=0,86400,60
    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(3000, 1, TSTRING(uint8_t * , "AT+MIPLOPEN=0,86400,60\r"));
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }

    for (i = 40; i > 0; i--) {
        NET_INF("wait for registration%d...", i);
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_REG_SUCCESS) {
                NET_INF("registered successfully");
                break;
            } else if (msg_at.info == AT_INFO_REG_FAILED) {
                NET_ERR("failed to register");
                return NET_ERR_UNKOWN;
            } else {
                NET_WAR("unexpected info %08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("registration timeout");
            return NET_ERR_UNKOWN;
        }
    }

    for (i = 30; i > 0; i--) {
        NET_INF("wait for discovery%d...", i);
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_DISCOVER) {
                NET_INF("discovered successfully");
                break;
            } else {
                NET_WAR("unexpected info %08x", msg_at.info);
            }
        } else if (i == 1) {
            NET_ERR("discovery timeout");
            return NET_ERR_UNKOWN;
        }
    }
    return NET_ERR_OK;
}

NET_ERR_CODE drv_m5310a_cm_send(uint8_t *pdata, uint32_t len) {
    uint8_t *pbuf;
    int32_t buf_len;
    NET_ERR_CODE ret;

    pbuf = pvPortMalloc(BASE64_ENCODE_LEN(len) + 64);
    if (pbuf == NULL) {
        NET_ERR("failed to alloc");
        return NET_ERR_UNKOWN;
    }
    //AT+MIPLNOTIFY=0,0,3348,0,5750,1,4,"test",0,0
    buf_len = snprintf((char *)pbuf, 64, "AT+MIPLNOTIFY=0,0,%d,%d,%s,1,%d,\"", CM_OBJ_ID, CM_INS_ID_UP, CM_RES_ID_STR, BASE64_ENCODE_LEN(len));
    if (buf_len < 0) {
        NET_ERR("failed to format");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    len = base64_encode(pdata, len, pbuf + buf_len, BASE64_ENCODE_LEN(len));
    if (len == 0) {
        NET_ERR("failed to encode by base64");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    buf_len += len;
    memcpy(pbuf + buf_len, CSTRING("\",0,0\r"));
    buf_len += (sizeof("\",0,0\r") - 1);

    ret = drv_net_write_bytes_raw(3000, 1, pbuf, buf_len);
    if (ret < 0) {
        NET_ERR("failed to send data");
        vPortFree(pbuf);
        return NET_ERR_UNKOWN;
    }
    vPortFree(pbuf);
    return NET_ERR_OK;
}
//AT+MIPLDELOBJ=0,3348
//AT+MIPLCLOSE=0
//AT+MIPLDELETE=0
NET_ERR_CODE drv_m5310a_cm_disconnect(void) {
    char pbuf[64];
    int32_t len;
    NET_ERR_CODE ret;
    //AT+MIPLDELOBJ=0,3348
    len = snprintf(pbuf, sizeof(pbuf), "AT+MIPLDELOBJ=0,%d\r", CM_OBJ_ID);
    ret = drv_net_write_bytes_raw(10000, 1, (uint8_t *)pbuf, len);
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }
    //AT+MIPLCLOSE=0
    ret = drv_net_write_bytes_raw(10000, 1, TSTRING(uint8_t *,"AT+MIPLCLOSE=0\r"));
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }
    //AT+MIPLDELETE=0
    ret = drv_net_write_bytes_raw(10000, 1, TSTRING(uint8_t *,"AT+MIPLDELETE=0\r"));
    if (ret < 0) {
        NET_ERR("");
        return ret;
    }
    return NET_ERR_OK;
}


NET_ERR_CODE drv_m5310a_cmd_sq_get(uint32_t *psq) {
    NET_ERR_CODE ret;
    MSG_AT msg_at;
    uint32_t i;
    xQueueReset(env_net.qinfo);
    ret = drv_net_write_bytes_raw(1000, 1, TSTRING(uint8_t * , "AT+CSQ\r"));
    if (ret < 0) {
        NET_ERR("failed to read sq");
        *psq = 0;
        return ret;
    }
    for (i = 5; i > 0; i--) {
        if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
            if (msg_at.info == AT_INFO_CSQ) {
                *psq = msg_at.value;
                ret = NET_ERR_OK;
                break;
            } else {
                NET_WAR("unexpected msg info:%08x", msg_at.info);
            }
        }
        if (i == 1) {
            NET_INF("read sq timeout");
            *psq = 0;
            return NET_ERR_UNKOWN;
        }
    }
    return ret;
}

NET_ERR_CODE drv_m5310a_executor(NET_CMD_CODE cmd, void *pdata) {
    if (cmd == NET_CMD_SQ_GET) {
        return drv_m5310a_cmd_sq_get((uint32_t *)pdata);
    } else if (cmd == NET_CMD_CT_ID_TYPE_GET) {
        *((NET_CT_ID_TYPE *)pdata) = NET_CT_ID_MAX;
        return NET_ERR_OK;
    } else {
        return NET_ERR_UNKOWN;
    }
}

NET_ERR_CODE drv_m5310a_try_to_sleep(void) {
    NET_ERR_CODE ret;
    uint32_t i;
    MSG_AT msg_at;
    for (i = 60; i > 0; i--) {
        NET_INF("m5310a try to sleep%d...", i);
        ret = drv_net_write_bytes_raw(10000, 1, TSTRING(uint8_t *,"AT+NPSMR?\r"));
        if (ret == NET_ERR_OK) {
            if (xQueueReceive(env_net.qinfo, &msg_at, 1000) == pdPASS) {
                if (msg_at.info == AT_INFO_NPSMR) {
                    NET_INF("enter psm successfully");
                    break;
                }
            }
        } else if (i == 1) {
            NET_ERR("enter psm timeout");
            return NET_ERR_UNKOWN;
        }
    }
    return NET_ERR_OK;
}

NET_ERR_CODE drv_m5310a_try_to_wake_up(void) {
    return drv_net_write_bytes_raw(500,5,TSTRING(uint8_t *,"AT\r"));;
}

NET_ERR_CODE drv_m5310a_power_set(NET_POWER_CONTROL net_power) {
    if (net_power == NET_POWER_ON) {
        env_net.pat_handlers = drv_m5310a_at_handlers;
        return drv_m5310a_try_to_get_ip();
    } else if (net_power == NET_POWER_OFF) {
        drv_net_power_off();
        return NET_ERR_OK;
    } else if (net_power == NET_POWER_SLEEP) {
        return drv_m5310a_try_to_sleep();
    } else if (net_power == NET_POWER_WAKE) {
        return drv_m5310a_try_to_wake_up();
    } else {
        NET_ERR("invalid power state");
        return NET_ERR_UNKOWN;
    }
}


//todo:drv_net_init()
const DRV_NET_INTERFACE drv_m5310a_interface = {
    .pf_power_set = drv_m5310a_power_set,
    .pf_executor = drv_m5310a_executor,
    //ct
    .pf_ct_connect = NULL,
    .pf_ct_send = NULL,
    .pf_ct_disconnect = NULL,
    //cm
    .pf_cm_connect = drv_m5310a_cm_connect,
    .pf_cm_send = drv_m5310a_cm_send,
    .pf_cm_disconnect = drv_m5310a_cm_disconnect,
    //socket
    .pf_socket_connect = drv_m5310a_socket_connect,
    .pf_socket_send = drv_m5310a_socket_send,
    .pf_socket_disconnect = drv_m5310a_socket_disconnect
};



